package cn.tedu.aop.demo;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 定义一个切面，用于在计算器类的方法中动态插入逻辑
 * 在Spring中一个切面类需要：
 * 添加@Component注解 首先必须被Spring管理
 * 添加@Aspect注解 用于标识它是一个切面类
 */
@Component
@Aspect
public class SystoutAspect {
    /*
    @Before注解用于标注该方法是在实际方法调用前切入并执行
    该注解中要传入一个参数，用于定义切入点（目标方法,target），就是实际调用的方法签名
     *
     * 切入点描述:
     * execution(方法签名)
     *
     * 方法签名格式示例:
     * public int cn.tedu.aop.demo.CalculatorImpl.add(..)
     * 修饰符 返回值类型 类完全限定名.方法名(参数列表)
     * 其中参数列表通常可以使用".."标识任意参数
     *
     * 方法名可以使用"*",标识该类的任意方法:
     * public int cn.tedu.aop.demo.CalculatorImpl.*(..)
     方法中可以接收一个参数：JoinPoint切入点
     该对象描述了当前切面切入的实际方法描述信息
     */
    //@Before("execution(public int cn.tedu.aop.demo.CalculatorImpl.*(..))")
    //@Before("execution(public int cn.tedu.aop.demo.CalculatorImpl.add(int,int))")
    //@Before("execution(public int cn.tedu.aop.demo.CalculatorImpl.add(int,double))")//切入点描述有误则无效
    //@Before("execution(public int cn.tedu.aop.demo.CalculatorImpl.add(..))")
    //参数类型任意，但是仍然只有int add(int,int)被切入，原因是其他add方法返回值类型也不同
    //@Before("execution(public * cn.tedu.aop.demo.CalculatorImpl.add(..))")
    //返回值类型可以使用"*"表示任意类型的返回值均可
    //@Before("execution(public double cn.tedu.aop.demo.CalculatorImpl.add(double ,double ))")
    //@Before("execution(public String cn.tedu.aop.demo.CalculatorImpl.add(java.lang.String ,java.lang.String ))")
    //如果参数或返回值为引用类型，也要使用完全限定名:包名.类名

    @Before("execution(public * cn.tedu.aop.demo.CalculatorImpl.*(..))")
    public void before(JoinPoint point){
        /*
        System.out.println("切面类在方法调用前被调用了！！！");
        //通过切入点对象获取实际调用的方法所属的对象
        Object target=point.getTarget();//CalculatorImpl实例
        System.out.println("target:"+target);
        //获取该类的名字？
        System.out.println(target.getClass().getName());//通过类对象获取该类的完全限定名
        //仅获取类名
        System.out.println(target.getClass().getSimpleName());
        //通过切入点获取实际调用的方法（方法签名）
        Signature signature=point.getSignature();
        String methodName=signature.getName();
        System.out.println("方法名："+methodName);
        Object [] args=point.getArgs();//通过切入点获取实参列表
        System.out.println("实参："+Arrays.toString(args));

         */
        String className=point.getTarget().getClass().getSimpleName();
        String methodName=point.getSignature().getName();
        System.out.println(className+"."+methodName+"():开始执行");
    }
    /*
    AfterReturning注解的切入时机为：方法调用完毕并返回了结果后调用
    该注解除了要定义切入点信息外，还可以传入一个参数：returning，用于指定实际方法
    调用完毕将结果返回后，把该返回结果传递到切面方法的那个参数上
    例如：
    returning=“returnValue”
     */
    @AfterReturning(value = "execution(public * cn.tedu.aop.demo.CalculatorImpl.*(..))",returning = "returnValue")
    public void afterReturning(JoinPoint point,Object returnValue){
        String className=point.getTarget().getClass().getSimpleName();
        String methodName=point.getSignature().getName();
        System.out.println(className+"."+methodName+"():执行完毕,"+"返回值是："+returnValue);
        //System.out.println("方法调用完毕并返回了结果后调用到切面了！！");
    }

    /**
     * @After注解是在目标方法执行完毕后调用的
     * 无论该方法执行过程中是否出现异常，最后都会执行
     *
     */
    @After("execution(public * cn.tedu.aop.demo.CalculatorImpl.*(..))")
    public void after(JoinPoint point){
        String className=point.getTarget().getClass().getSimpleName();
        String methodName=point.getSignature().getName();
        System.out.println(className+"."+methodName+"():执行完毕");
    }
    @AfterThrowing(value = "execution(public * cn.tedu.aop.demo.CalculatorImpl.*(..))",throwing = "e")
    public void afterThrowing(JoinPoint point,Exception e){
        String className=point.getTarget().getClass().getSimpleName();
        String methodName=point.getSignature().getName();
        System.out.println(className+"."+methodName+"():抛出异常了,异常是："+e.getMessage());
    }
    @Around("execution(public * cn.tedu.aop.demo.CalculatorImpl.*(..))")
    public Object around(ProceedingJoinPoint point){//处理中的切入点
        String className=point.getTarget().getClass().getSimpleName();
        String method=point.getSignature().getName();
        Object[]args=point.getArgs();
        try {
            //这里就是@Before的操作
            System.out.println(className+"."+method+"():开始调用方法...");

            //调用实际方法并接收返回值
            Object returnValue = point.proceed();//通过切入点调用实际方法:calculator.add()

            //这里就是@AfterReturning的操作
            System.out.println(className+"."+method+"():方法执行完毕，返回值为:"+returnValue);

            return returnValue;
        } catch (Throwable e) {
            //这里就是@AfterThrowing的操作
            System.out.println(className+"."+method+"():方法抛出异常了，异常是:"+e.getMessage());
            throw new RuntimeException(e);
        } finally {
            //这里就是@After的操作
            System.out.println(className+"."+method+"():方法执行完毕了");
        }
    }
}
